package com.leyou.item.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.entity.Category;
import com.leyou.item.entity.Sku;
import com.leyou.item.entity.Spu;
import com.leyou.item.entity.SpuDetail;
import com.leyou.item.mapper.SkuMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class GoodsService extends ServiceImpl<SkuMapper, Sku> {

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SpuDetailMapper detailMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    public PageResult<SpuDTO> spuPageQuery(Integer page, Integer rows, String key, Boolean saleable) {
        //添加分页条件
        IPage<Spu> ipage = new Page<>(page, rows);
        //提供封装条件的对象
        QueryWrapper<Spu> wrapper = Wrappers.query();
        //判断是否有搜索关键字
        if(StringUtils.isNotEmpty(key)){
            wrapper.like("name", key);
        }
        //判断是否有上下架要求，mysql中用tinyint表示布尔值，凡是非0都对应java中的true，0对应java中的false
        if(saleable!=null){
            wrapper.eq("saleable", saleable);
        }
        //执行分页查询
        ipage = spuMapper.selectPage(ipage, wrapper);
        //先获取Spu数据列表
        List<Spu> spus = ipage.getRecords();
        //判空
        if(CollectionUtils.isEmpty(spus)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //将Spu集合转成SpuDTO集合
        List<SpuDTO> spuDTOS = BeanHelper.copyWithCollection(spus, SpuDTO.class);
        //给SpuDTO中每个对象添加分类名称和品牌名称的值
        handlerCategoryNameAndBrandName(spuDTOS);
        //封装自定义分页对象
        PageResult<SpuDTO> pageResult = new PageResult<>(
                ipage.getTotal(),
                ipage.getPages(),
                spuDTOS
        );
        return pageResult;
    }

    //给SpuDTO中每个对象添加分类名称和品牌名称的值
    private void handlerCategoryNameAndBrandName(List<SpuDTO> spuDTOS) {
        //遍历
        spuDTOS.forEach(spuDTO -> {
            //根据品牌id获取品牌名称
            String brandName = brandService.getBrandById(spuDTO.getBrandId()).getName();
            //把品牌名称赋值给spuDTO对象
            spuDTO.setBrandName(brandName);

            //根据分类id的集合获取分类名称的集合
            String categoryNames = categoryService.findCategoryByIds(spuDTO.getCategoryIds())//获取三个分类对象的集合
                    .stream()//转流
                    .map(Category::getName)//收集到每个分类对象中的分类名称，最终获取到三个零散的分类名称字符串
                    .collect(Collectors.joining("|"));//将所有收集到的分类名称收集成为以竖杠分割的字符串
            //把三级分类的名称赋值给spuDTO对象
            spuDTO.setCategoryName(categoryNames);
        });
    }

    public void saveGoods(SpuDTO spuDTO) {
        try {
            //得到Spu对象
            Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
            //保证新增商品默认是下架的状态
            spu.setSaleable(false);
            //新增商品
            spuMapper.insert(spu);

            //得到SpuDetail对象
            SpuDetail spuDetail = spuDTO.getSpuDetail();
            //给主键赋值
            spuDetail.setSpuId(spu.getId());
            //新增商品详情
            detailMapper.insert(spuDetail);

            //得到Sku的集合
            List<Sku> skus = spuDTO.getSkus();
            //循环给每个Sku中的SpuId赋值
            skus.forEach(sku -> {
                sku.setSpuId(spu.getId());
            });
            //新增sku集合
            saveBatch(skus);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }

    public void updateSaleable(Long id, Boolean saleable) {
        try {
            //封装修改条件对象，其中对象中的id必须赋值
            Spu entity = new Spu();
            entity.setId(id);
            entity.setSaleable(saleable);
            spuMapper.updateById(entity);

            //通过feign调用search微服务和page微服务，同步确保数据同步
            //通过MQ异步去维护search微服务和page微服务的数据同步
            //根据当前是上架或者下架操作来指定routingKey
            String routingKey = saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY;
            amqpTemplate.convertAndSend(MQConstants.Exchange.ITEM_EXCHANGE_NAME,
                    routingKey,
                    id);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }

    public List<Sku> findSkusBySpuId(Long id) {
        //封装条件
        Sku entity = new Sku();
        entity.setSpuId(id);
        Wrapper<Sku> wrapper = Wrappers.query(entity);
        //执行查询
        List<Sku> list = skuMapper.selectList(wrapper);
        if(CollectionUtils.isEmpty(list)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return list;
    }

    public SpuDetail findSpuDetailById(Long id) {
        SpuDetail spuDetail = detailMapper.selectById(id);
        if(spuDetail==null){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }

    public SpuDTO findSpuDTOBySpuId(Long id) {
        //根据主键查询Spu
        Spu spu = spuMapper.selectById(id);
        //判空
        if(spu==null){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //将Spu对象转成SpuDTO对象
        SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
        //根据spuId查询SpuDetail对象
        SpuDetail spuDetail = findSpuDetailById(id);
        //将SpuDetail赋值给SpuDTO中对应的属性
        spuDTO.setSpuDetail(spuDetail);
        //根据SpuId查询Sku集合
        List<Sku> skus = findSkusBySpuId(id);
        //将Skus赋值给SpuDTO中对应的属性
        spuDTO.setSkus(skus);
        return spuDTO;
    }

    public List<Sku> findSkusByIds(List<Long> ids) {
        List<Sku> skus = skuMapper.selectBatchIds(ids);
        if(CollectionUtils.isEmpty(skus)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skus;
    }

    //减库存
    public void minusStock(Map<Long, Integer> paramMap) {
        try {
            paramMap.entrySet().forEach(entry->{
                Long skuId = entry.getKey();
                Integer num = entry.getValue();

                //根据id查询sku
                Sku sku = skuMapper.selectById(skuId);

                //注意，根据id做修改时，封装的条件对象中id一定要有值
                Sku entity = new Sku();
                entity.setId(skuId);
                entity.setStock(sku.getStock()-num);
                skuMapper.updateById(entity);
            });
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

    }
    //增加库存
    public void plusStock(Map<Long, Integer> paramMap) {
        try {
            paramMap.entrySet().forEach(entry->{
                Long skuId = entry.getKey();
                Integer num = entry.getValue();

                //根据id查询sku
                Sku sku = skuMapper.selectById(skuId);

                //注意，根据id做修改时，封装的条件对象中id一定要有值
                Sku entity = new Sku();
                entity.setId(skuId);
                entity.setStock(sku.getStock()+num);
                skuMapper.updateById(entity);
            });
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }
}
